﻿// Copyright 2010 Maxime Serrano <lastchronicler@gmail.com>
// Copyright 2009, 2010 Matvei Stefarov <me@matvei.org>
using System;
using System.Text;
using System.Net;

namespace ForgeCraft
{
    public sealed class PlayerInfo
    {

        public const int fieldCount = 27;
        public const string DateFormat = "o";

        public string name;
        public IPAddress lastIP;
        public Group group;
        public DateTime classChangeDate;
        public string classChangedBy;

        public bool banned;
        public DateTime banDate;
        public string bannedBy;
        public DateTime unbanDate;
        public string unbannedBy;
        public string banReason;
        public string unbanReason;
        public Position homePos;

        public DateTime lastFailedLoginDate;
        public IPAddress lastFailedLoginIP;
        public short failedLoginCount;
        public DateTime firstLoginDate;
        public DateTime lastLoginDate;

        public TimeSpan totalTimeOnServer;
        public int blocksBuilt;
        public int blocksDeleted;
        public short timesVisited;
        public int linesWritten;
        public short thanksReceived;
        public short warningsReceived;

        public string inventory;
        public int health;


        // === Serialization & Defaults =======================================
        // fabricate info for a player
        public PlayerInfo(string _name, Group _group)
        {
            name = _name;
            lastIP = IPAddress.None;

            group = _group;
            classChangeDate = DateTime.MinValue;
            classChangedBy = "-";

            banDate = DateTime.MinValue;
            bannedBy = "-";
            unbanDate = DateTime.MinValue;
            unbannedBy = "-";
            banReason = "-";
            unbanReason = "-";

            firstLoginDate = DateTime.Now;
            lastLoginDate = DateTime.Now;

            lastFailedLoginDate = DateTime.MinValue;
            lastFailedLoginIP = IPAddress.None;

            totalTimeOnServer = new TimeSpan(0);
            timesVisited = 1;
            inventory = "-";
            health = 20;
        }


        // generate info for a new player
        public PlayerInfo(Player player)
        {
            name = player.name;
            lastIP = player.session.GetIP();

            group = Group.findPlayerGroup(player.name);
            classChangeDate = DateTime.MinValue;
            classChangedBy = "-";

            banDate = DateTime.MinValue;
            bannedBy = "-";
            unbanDate = DateTime.MinValue;
            unbannedBy = "-";
            banReason = "-";
            unbanReason = "-";

            homePos = Server.worlds.Find(world => world.name == Config.GetString(ConfigKey.World)).spawn;

            firstLoginDate = DateTime.Now;
            lastLoginDate = firstLoginDate;

            lastFailedLoginDate = DateTime.MinValue;
            lastFailedLoginIP = IPAddress.None;

            totalTimeOnServer = new TimeSpan(0);
            timesVisited = 1;
            inventory = "-";
            health = 20;
        }


        // load info from file
        public PlayerInfo(string[] fields)
        {
            name = fields[0];
            lastIP = IPAddress.Parse(fields[1]);

            group = Group.Find(fields[2]);
            if (group == null)
            {
                group = Group.findPerm(Permission.Guest);
                Logger.Log("PlayerInfo: Could not parse class for player {0}. Setting to default ({1}).", LogType.Error, name, group.name);
            }
            if (fields[3] != "-") classChangeDate = DateTime.Parse(fields[3]);
            else classChangeDate = DateTime.MinValue;
            classChangedBy = fields[4];

            banned = (fields[5] == "b");

            if (fields[6] != "-") banDate = DateTime.Parse(fields[6]);
            else banDate = DateTime.MinValue;
            bannedBy = fields[7];
            if (fields[8] != "-") unbanDate = DateTime.Parse(fields[8]);
            else unbanDate = DateTime.MinValue;
            unbannedBy = fields[9];
            banReason = UnEscape(fields[10]);
            unbanReason = UnEscape(fields[11]);

            if (fields[12] != "-") lastFailedLoginDate = DateTime.Parse(fields[12]);
            else lastFailedLoginDate = DateTime.MinValue;
            if (fields[13] != "-") lastFailedLoginIP = IPAddress.Parse(fields[13]);
            else lastFailedLoginIP = IPAddress.None;
            failedLoginCount = Int16.Parse(fields[14]);

            firstLoginDate = DateTime.Parse(fields[15]);
            lastLoginDate = DateTime.Parse(fields[16]);
            totalTimeOnServer = TimeSpan.Parse(fields[17]);

            blocksBuilt = Int32.Parse(fields[18]);
            blocksDeleted = Int32.Parse(fields[19]);
            timesVisited = Int16.Parse(fields[20]);
            linesWritten = Int32.Parse(fields[21]);
            thanksReceived = Int16.Parse(fields[22]);
            warningsReceived = Int16.Parse(fields[23]);
            inventory = fields[24];
            health = Int32.Parse(fields[25]);

            homePos.x = double.Parse(fields[26].Split(':')[0]);
            homePos.y = double.Parse(fields[26].Split(':')[1]);
            homePos.z = double.Parse(fields[26].Split(':')[2]);
        }


        // save to file
        public string Serialize()
        {
            string[] fields = new string[fieldCount];

            fields[0] = name;
            fields[1] = lastIP.ToString();

            fields[2] = group.name.ToString();
            if (classChangeDate == DateTime.MinValue) fields[3] = "-";
            else fields[3] = classChangeDate.ToString(DateFormat);
            fields[4] = classChangedBy;

            if (banned) fields[5] = "b";
            else fields[5] = "-";
            if (banDate == DateTime.MinValue) fields[6] = "-";
            else fields[6] = banDate.ToString(DateFormat);
            fields[7] = bannedBy;
            if (unbanDate == DateTime.MinValue) fields[8] = "-";
            else fields[8] = unbanDate.ToString(DateFormat);
            fields[9] = unbannedBy;
            fields[10] = Escape(banReason);
            fields[11] = Escape(unbanReason);

            if (lastFailedLoginDate == DateTime.MinValue) fields[12] = "-";
            else fields[12] = lastFailedLoginDate.ToString(DateFormat);
            if (lastFailedLoginIP == IPAddress.None) fields[13] = "-";
            else fields[13] = lastFailedLoginIP.ToString();
            fields[14] = failedLoginCount.ToString();

            fields[15] = firstLoginDate.ToString(DateFormat);
            fields[16] = lastLoginDate.ToString(DateFormat);
            fields[17] = totalTimeOnServer.ToString();

            fields[18] = blocksBuilt.ToString();
            fields[19] = blocksDeleted.ToString();
            fields[20] = timesVisited.ToString();
            fields[21] = linesWritten.ToString();
            fields[22] = thanksReceived.ToString();
            fields[23] = warningsReceived.ToString();
            fields[24] = inventory.ToString();
            fields[25] = health.ToString();
            fields[26] = homePos.x.ToString() + ":" + homePos.y.ToString() + ":" + homePos.z.ToString();

            return String.Join(",", fields);
        }


        // === Updating =======================================================

        // update information
        public void ProcessLogin(Player player)
        {
            name = player.name;
            group = Group.findPlayerGroup(player.name);
            lastIP = player.session.GetIP();
            lastLoginDate = DateTime.Now;
            timesVisited++;
        }


        public void ProcessFailedLogin(Player player)
        {
            lastFailedLoginDate = DateTime.Now;
            lastFailedLoginIP = player.session.GetIP();
            failedLoginCount++;
        }

        public void InventoryLoad(Player player)
        {
            inventory = player.info.inventory;

            string[] invItemsList = inventory.Split('|');

            //slot,item,count,damage|slot,item,count,damage|slot,item,count,damage
            foreach (string s in invItemsList)
            {
                if (s.Length > 2)
                {
                    string[] invItem = s.Split(':');

                    short slot = -1, type = 0, damage = 0;
                    byte count = 0;

                    slot = short.Parse(invItem[0]);
                    type = short.Parse(invItem[1]);
                    count = byte.Parse(invItem[2]);
                    damage = short.Parse(invItem[3]);

                    if (slot < 0) {
                        Logger.Log("PlayerInventory: Could not parse slot for player {0}:{1}.).", LogType.Error, name, invItem[0]);
                        break;
                    }

                    if (type == 0 || count == 0)
                    {
                        Logger.Log("PlayerInventory: Invalid item for player {0}:{1}:{2}.).", LogType.Error, name, invItem[1], invItem[2]);
                        break;
                    }

                    player.Inventory.SetSlot(slot, new InventoryItem(type, count, damage));
                }
            }
        }

        public void InventorySave(Player player)
        {
            string invString = "";

            for (short i = 0; i < player.Inventory.slots.Length; i++)
            {
                if (player.Inventory.slots[i].Type > 0)
                {
                    invString += i + ":" + player.Inventory.slots[i].Type + ":" + player.Inventory.slots[i].Count + ":" + player.Inventory.slots[i].Damage + "|";
                }
            }

            player.info.inventory = invString;
        }


        public void ProcessLogout(Player player)
        {
            totalTimeOnServer += DateTime.Now.Subtract(player.session.loginTime);
        }


        public bool ProcessBan(string _bannedBy, string _banReason)
        {
            if (!banned)
            {
                banned = true;
                bannedBy = _bannedBy;
                banDate = DateTime.Now;
                banReason = _banReason;
                return true;
            }
            else
            {
                return false;
            }
        }

        public bool ProcessUnBan(string _unbannedBy, string _unbanReason)
        {
            if (banned)
            {
                banned = false;
                unbannedBy = _unbannedBy;
                unbanDate = DateTime.Now;
                unbanReason = _unbanReason;
                return true;
            }
            else
            {
                return false;
            }
        }

        public void ProcessBlockBuild(byte type)
        {
            if (type == 0)
            {
                blocksDeleted++;
            }
            else
            {
                blocksBuilt++;
            }
        }



        // === Utils ==========================================================
        public static string Escape(string str)
        {
            return str.Replace("\\", "\\\\").Replace("'", "\\'");
        }

        public static string UnEscape(string str)
        {
            return str.Replace("\\'", "'").Replace("\\\\", "\\");
        }
    }
}